﻿namespace Bootsy.PEG

abstract class CompositeRule(IGrammarRule):
	_rules as (IGrammarRule)
	
	def constructor(*rules as (IGrammarRule)):
		_rules = rules
		
class SequenceRule(CompositeRule):
	def constructor(*rules as (IGrammarRule)):
		super(*rules)
	
	def Apply(parser as IParser):
		actions = array(Action, len(_rules))
		i = 0
		for rule in _rules:
			action = parser.Evaluate(rule)
			if action is null:
				return null
			actions[i++] = action
		return do:
			for action in actions:
				action()
		
class AlternationRule(CompositeRule):
	def constructor(*rules as (IGrammarRule)):
		super(*rules)
	
	def Apply(parser as IParser):
		for rule in _rules:
			action = parser.Evaluate(rule)
			if action is not null:
				return action
		return null
		
class ParserConstants:
	public static final Success as Action = do:
		pass

class EOFRule(IGrammarRule):
	public static final Instance = EOFRule()
	
	def Apply(parser as IParser):
		if parser.Input.MoveNext(): return null
		return ParserConstants.Success
		
class LiteralRule(IGrammarRule):
	_action as StringHandler
	_literal as string
	
	def constructor(literal as string, action as StringHandler):
		_literal = literal
		_action = action
		
	def Apply(parser as IParser):
		input = parser.Input
		for ch in _literal:
			if not input.MoveNext():
				return null
			if ch != input.Current:
				return null
		if _action is null:
			return ParserConstants.Success
		return do:
			_action(_literal, parser.Context)
		
callable StringHandler(value as string, context)
callable IntHandler(value as int, context)
		
class RegexRule(IGrammarRule):

	_re as regex
	_action as StringHandler

	def constructor(re as regex, action as StringHandler):
		assert re.ToString().StartsWith("^")
		_re = re
		_action = action
		
	def Apply(parser as IParser):
		input = parser.Input
		
		position = input.Position+1
		match = _re.Match(input.Text[position:])
		if match.Success:
			parser.Input.Reset(position + match.Length)
			if _action is null:
				return ParserConstants.Success
			return do:
				_action(match.Value.Trim(), parser.Context)
		return null
		
class RemainingRule(IGrammarRule):
	
	_action as StringHandler

	def constructor(action as StringHandler):
		_action = action
		
	def Apply(parser as IParser):
		buffer = System.Text.StringBuilder()
		for ch in parser.Input:
			buffer.Append(ch)
		return do:
			_action(buffer.ToString(), parser.Context)
	
class IntegerRule(IGrammarRule):
	
	_action as IntHandler
	
	def constructor(action as IntHandler):
		_action = action
		
	def Apply(parser as IParser):
		buffer = System.Text.StringBuilder()
		mark = parser.Input.Mark()
		for ch in parser.Input:
			if not char.IsNumber(ch):
				break
			buffer.Append(ch)
			mark = parser.Input.Mark()
		parser.Input.Reset(mark)
		
		if buffer.Length == 0: return null
		if _action is null: return ParserConstants.Success
		return do:
			_action(int.Parse(buffer.ToString()), parser.Context)
				
[Extension]
def op_ShiftRight(lhs as IGrammarRule, rhs as IGrammarRule):
	return sequence(lhs, rhs)
	
[Extension]
def op_BitwiseOr(lhs as IGrammarRule, rhs as IGrammarRule):
	return alternation(lhs, rhs)
	
def literal(s as string):
	return LiteralRule(s, null)
	
def literal(s as string, handler as StringHandler):
	return LiteralRule(s, handler)
	
def alternation(lhs as IGrammarRule, rhs as IGrammarRule):
	return AlternationRule(lhs, rhs)
	
def sequence(lhs as IGrammarRule, rhs as IGrammarRule):
	return SequenceRule(lhs, rhs)
	
def re(e as regex, handler as StringHandler):
	return RegexRule(e, handler)
	
def re(e as regex):
	return RegexRule(e, null)
	
def integer():
	return IntegerRule(null)
	
def integer(handler as IntHandler):
	return IntegerRule(handler)
	
def eof():
	return EOFRule.Instance
	
def remaining(handler as StringHandler):
	return RemainingRule(handler)
	
def matches(rule as IGrammarRule, text as string):
	return parse(rule, text) is not null 

def parse(rule as IGrammarRule, text as string):
	parser = StringParser(text)
	return parser.Evaluate(rule)
